Feedback on Self-Hosting and Using n8n
Today, I self-hosted n8n to explore its capabilities. The self-hosting process was straightforward, and I was looking at this tool for various automation use cases within our organization.
To test its functionality, I set up a simple workflow where n8n would collect issue details and create a Jira ticket. This use case is highly relevant to our organization, as manually setting it up via our admin portal and Jira API integration would have taken approximately 4-5 hours. Configuring credentials for both Jira and AWS was seamless.
However, challenges arose when I attempted to upload a file to AWS S3. It took me nearly six hours to figure out the correct approach for handling binary file uploads, a task that would have been much faster had I implemented it directly in the backend.
Areas for Improvement
- Limited Documentation Beyond Installation – While setting up the tool was well-documented, deeper workflow concepts and advanced configurations lacked sufficient guidance.
- Unclear Data Flow – Despite successfully completing the workflow, I still find the data flow within n8n difficult to comprehend fully.
Challenges Encountered
- Uploading binary data to AWS S3 – The process was not intuitive and required extensive troubleshooting.
- Aggregating multiple URLs into a single string – I struggled to collect and format multiple image URLs effectively.
- Unexpected Ticket Creation Behavior – When uploading six images, n8n created six separate Jira tickets instead of associating all images with a single ticket. This was unexpected and required additional workarounds.
Final Thoughts
While n8n is a promising automation tool, my expectation was that a non-technical team member, such as a PM, could handle some of the automation tasks to reduce the load on the tech team. However, I ended up writing a significant amount of code, which somewhat defeated the purpose of using a no-code/low-code platform.
I am attaching my workflow diagram and would appreciate any insights on whether I have unnecessarily overcomplicated the setup.
code block 1
// Extract all binary fields dynamically
const items = $input.all();
let newItems = [];
for (const item of items) {
if (item.binary) {
for (const key in item.binary) {
let fileName = `${key}.${item.binary[key]["fileExtension"]}`;
console.log("fileName",fileName)
newItems.push({
json: {
fileName:fileName,
...item.json,
url:`https://example.com/jira-tickets/${fileName}`,
},
binary: {
data: item.binary[key]
}
});
}
}
}
return newItems;
Code Block 2
const items = $input.all();
let mergedItem = {
json: {
email:"",
issueTitle:"",
issueDescription:"",
project:"",
reportedBy:"",
partnerId:"",
fileNames: [],
urls: [],
},
binary: {}
};
for (const item of items) {
for (const key in item.binary) {
let fileName = `${key}.${item.binary[key]["fileExtension"]}`;
mergedItem.json.email = item.json.Email;
mergedItem.json.issueTitle = item.json['Issue Title'];
mergedItem.json.issueDescription = item.json['Issue Description'];
mergedItem.json.project = item.json["Project"];
mergedItem.json.reportedBy = item.json[
"Reported By"
]
mergedItem.json.partnerId = item.json[
"Partner Id"
]
mergedItem.json.fileNames.push(fileName);
mergedItem.json.urls.push(`https://dlxpk8rzj7ccz.cloudfront.net/jira-tickets/${fileName}`);
mergedItem.binary[key] = item.binary[key]; // Preserve binary data
}
}
// Return a single item that contains all files
return [mergedItem];